/**
 * 按位运算符
 * C++ 提供了 6 个位操作运算符，通常称为按位运算符
 *   左移	  <<	x << y	  x 中的所有位左移 y 位
 *   右移	  >>	x >> y	  x 中的所有位右移 y 位
 *   按位非	  ~	    ~x	      x 中的所有位都翻转
 *   按位与	  &	    x & y	  x 中的每一位和 y 中的每一位
 *   按位或	  |	    x | y	  x 中的每一位或 y 中的每一位
 *   按位异或 ^	    x ^ y	  x 中的每一位异或 y 中的每一位
 * 
 * 最佳实践
 *   为了避免意外，请使用带有无符号操作数的按位运算符或 std::bitset。
 * 
 * 按位左移 (<<) 和按位右移 (>>) 运算符
 * 按位左移(<<) 运算符将位向左移动。左操作数是要移动位的表达式，右操作数是要左移的整数位。
 * 因此，当我们说 时x << 1，我们是在说“将变量 x 中的位向左移动 1 位”。从右侧移入的新位接收值 0。
 *   0011 << 1 为 0110
 *   0011 << 2 为 1100
 *   0011 << 3 为 1000
 * 请注意，在第三种情况下，我们稍微偏离了数字的末尾！从二进制数末尾移出的位将永远丢失。
 * 
 * 按位右移(>>) 运算符将位向右移动。
 *   1100 >> 1 为 0110
 *   1100 >> 2 为 0011
 *   1100 >> 3 为 0001
 * 请注意，在第三种情况下，我们将数字的右端移了一点，因此它丢失了。
 * 
 * C++ 中的位移位与字节序无关。左移始终向最高有效位移动，右移始终向最低有效位移动。
 * 
 * 运算符<<和运算符>>不是用于输入和输出吗？
 *   他们确实是。
 *   如今的程序通常不会过多使用按位左移和右移运算符来移位。相反，您倾向于看到按位左移运算符与 std::cout （或其他流对象）一起使用来输出文本。
 * 
 *   operator<< 如何知道在一种情况下移位并在另一种情况下输出x？答案是 std::cout 已重载（提供了替代定义）operator<<，它执行控制台输出而不是位移。
 *   当编译器看到operator<<的左操作数是std::cout时，它就知道应该调用std::cout重载的operator<<版本来进行输出。如果左操作数是整型，则运算符 << 知道它应该执行其通常的位移行为。
 *   这同样适用于运算符>>。
 * 
 * 如果您使用运算符 << 进行输出和左移，则需要括号
 * 
 * 按位非
 *   按位非运算符 (~) 可能是所有按位运算符中最容易理解的。它只是将每个位从 0 翻转为 1，反之亦然。请注意，按位 NOT的结果取决于数据类型的大小。
 * 翻转 4 位：
 *   ~0100 为 1011
 * 翻转 8 位：
 *   ~0000 0100 为 1111 1011
 * 在 4 位和 8 位情况下，我们从相同的数字开始（二进制 0100 与 0000 0100 相同，就像十进制 7 与 07 相同），但最终得到不同的结果。
 * 
 * 按位或
 *   按位或(|) 的工作方式与其对应的逻辑或非常相似。然而，不是对操作数应用“或”来产生单个结果，而是对每个位应用“按位或” 
 *   0 1 0 1 或
 *   0 1 1 0
 *   --------
 *   0 1 1 1
 * 
 * 按位与
 *   按位与(&) 的工作方式与上面类似。如果左操作数和右操作数的计算结果都为 true ，则逻辑 AND 的计算结果为true。
 *   如果列中的两个位均为1 ，则按位 AND计算结果为true (1)。考虑这个表达式。将每个位排列起来并对每列位应用 AND 运算：0b0101 & 0b0110
 *   0 1 0 1 与
 *   0 1 1 0
 *   --------
 *   0 1 0 0
 * 
 * 按位异或
 *   当计算两个操作数时，如果一个且只有一个操作数为true (1)，则 XOR 计算结果为true (1)。如果两者都不为 true 或两者都不为 true，则计算结果为0。
 *   0 1 1 0 异或
 *   0 0 1 1
 *   --------
 *   0 1 0 1
 * 
 * 
 * 按位赋值运算符
 *   <<=  >>=  |=  &=  ^=
 *   
 * 
 */


#include <bitset>
#include <iostream>

int main()
{
    std::bitset<4> x { 0b1100 };

    std::cout << x << '\n';
    std::cout << (x >> 1) << '\n'; // shift right by 1, yielding 0110
    std::cout << (x << 1) << '\n'; // shift left by 1, yielding 1000


    unsigned int x1 { 0b0100 };
    x1 = x1 << 1; // use operator<< for left shift
    std::cout << std::bitset<4>{ x1 } << '\n'; // use operator<< for output


    std::bitset<4> x2{ 0b0110 };

	std::cout << x2 << 1 << '\n'; // print value of x2 (0110), then 1
	std::cout << (x2 << 1) << '\n'; // print x2 left shifted by 1 (1100)


    std::cout << ~std::bitset<4>{ 0b0100 } << ' ' << ~std::bitset<8>{ 0b0100 } << '\n';


    std::cout << (std::bitset<4>{ 0b0101 } | std::bitset<4>{ 0b0110 }) << '\n';


    std::cout << (std::bitset<4>{ 0b0101 } & std::bitset<4>{ 0b0110 }) << '\n';


    std::cout << (std::bitset<4>{ 0b0110 } ^ std::bitset<4>{ 0b0011 }) << '\n';

    return 0;
}